﻿/*
 *  Copyright 2012 Cloudbase Solutions Srl
 *
 *  This program is free software; you can redistribute it and/or modify
 *  it under the terms of the Microsoft Public License (Ms-PL).  
 */

using System;
using System.Collections.Generic;
using System.IO;
using System.Net;
using System.Text;
using System.Text.RegularExpressions;
using Cloudbase.Iis.HostsFileManager.Model;

namespace Cloudbase.Iis.Utility
{
    public static class HostsFileManagerUtility
    {
        static Regex re = new Regex(@"^\s*([0-9a-fA-F\:\.]+)\s+((?:(?:[a-zA-Z]|[a-zA-Z][a-zA-Z0-9\-]*[a-zA-Z0-9])\.)*(?:[A-Za-z]|[A-Za-z][A-Za-z0-9\-]*[A-Za-z0-9]))\s*(?:\#\s*((?:\w+\s*)+)){0,1}$");

        public static void RemoveHost(string host)
        {
            UpdateOrRemoveHost(false, host, null, null, null);
        }

        private static void UpdateOrRemoveHost(bool isUpdate, string originalHost, string newHost, IPAddress ipAddress, string comment)
        {
            IList<string> outputLines = new List<string>();

            string hostsPath = GetHostsFilePath();
            if (File.Exists(hostsPath))
                foreach (var line in File.ReadAllLines(hostsPath))
                {
                    Match m = re.Match(line);
                    bool found = false;
                    if (m.Success && originalHost.Equals(m.Groups[2].Value, StringComparison.InvariantCultureIgnoreCase))
                        found = true;

                    if(!found)
                        outputLines.Add(line);
                    else if(isUpdate)
                        outputLines.Add(GetHostEntryLine(newHost, ipAddress, comment));
                }

            string[] ar = new string[outputLines.Count];
            outputLines.CopyTo(ar, 0);
            File.WriteAllLines(hostsPath, ar);
        }

        public static void UpdateHost(string originalHost, string host, IPAddress ipAddress, string comment)
        {
            UpdateOrRemoveHost(true, originalHost, host, ipAddress, comment);
        }

        public static void AddHost(string host, IPAddress ipAddress, string comment)
        {
            if (! ContainsHost(host))
            {
                string hostsPath = GetHostsFilePath();

                StringBuilder sb = new StringBuilder();
                if (File.Exists(hostsPath) && !File.ReadAllText(hostsPath).EndsWith(Environment.NewLine))
                    sb.Append(Environment.NewLine);
                sb.Append(GetHostEntryLine(host, ipAddress, comment));
                sb.AppendLine();

                File.AppendAllText(hostsPath, sb.ToString());
            }
        }

        private static string GetHostEntryLine(string host, IPAddress ipAddress, string comment)
        {
            StringBuilder sb = new StringBuilder();
            sb.Append(string.Format("{0} {1}", ipAddress, host));

            if (comment != null && comment.Trim().Length > 0)
                sb.Append(string.Format(" # {0}", comment));
            return sb.ToString();
        }

        public static bool ContainsHost(string host)
        {
            var hosts = GetAllEntries();
            foreach (var h in hosts)
                if (h.Host.Equals(host, StringComparison.InvariantCultureIgnoreCase))
                    return true;
            return false;
        }

        public static IList<HostFileEntry> GetAllEntries()
        {
            IList<HostFileEntry> l = new List<HostFileEntry>();

            string hostsPath = GetHostsFilePath();
            if (File.Exists(hostsPath))
                foreach (var line in File.ReadAllLines(hostsPath))
                {
                    Match m = re.Match(line);
                    if (m.Success)
                    {
                        string ip = m.Groups[1].Value;
                        string host = m.Groups[2].Value;
                        string comment = m.Groups[3].Value;

                        IPAddress ipAddr;
                        if (IPAddress.TryParse(ip, out ipAddr))
                        {
                            l.Add(new HostFileEntry()
                            {
                                Host = host,
                                IPAddress = ipAddr,
                                Comment = comment
                            });
                        }
                    }
                }

            return l;
        }

        public static string GetHostsFilePath()
        {
            return Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.System), @"drivers\etc\hosts");
        }
    }
}
